package io.kiki.sba.registry.server.metadata.resource;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.kiki.sba.registry.common.model.Node.NodeType;
import io.kiki.sba.registry.common.model.console.PersistenceData;
import io.kiki.sba.registry.common.model.console.PersistenceDataBuilder;
import io.kiki.sba.registry.common.model.constants.ValueConstants;
import io.kiki.sba.registry.common.model.metaserver.ProvideDataChangeEvent;
import io.kiki.sba.registry.common.model.sessionserver.GrayOpenPushSwitchRequest;
import io.kiki.sba.registry.core.model.Result;
import io.kiki.sba.registry.server.metadata.provide.data.DefaultProvideDataNotifier;
import io.kiki.sba.registry.server.metadata.provide.data.ProvideDataService;
import io.kiki.sba.registry.server.metadata.resource.filter.AuthRestController;
import io.kiki.sba.registry.server.metadata.resource.filter.LeaderAwareRestController;
import io.kiki.sba.registry.util.JsonUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;


@Path("stopPushDataSwitch")
@LeaderAwareRestController
public class StopPushDataResource {

    private static final Logger logger = LoggerFactory.getLogger(StopPushDataResource.class);


    @Autowired
    private ProvideDataService provideDataService;

    @Autowired
    private DefaultProvideDataNotifier provideDataNotifier;

    @GET
    @Path("open")
    @Produces(MediaType.APPLICATION_JSON)
    @AuthRestController
    public Result closePush() {
        boolean ret;
        Result result = new Result();

        ret = resetGrayOpenPushSwitch();
        if (!ret) {
            result.setSuccess(false);
            return result;
        }

        PersistenceData persistenceData = PersistenceDataBuilder.createPersistenceData(ValueConstants.STOP_PUSH_DATA_SWITCH_DATA_ID, "true");

        try {
            ret = provideDataService.saveProvideData(persistenceData);
            logger.info("open stop push data switch to DB result {}!", ret);
        } catch (Throwable e) {
            logger.error("error open stop push data switch to DB!", e);
            throw new RuntimeException("open stop push data switch to DB error!", e);
        }

        if (ret) {
            fireDataChangeNotify(persistenceData.getVersion(), ValueConstants.STOP_PUSH_DATA_SWITCH_DATA_ID);
        }

        result.setSuccess(ret);
        return result;
    }

    /**
     * open push
     */
    @GET
    @Path("close")
    @Produces(MediaType.APPLICATION_JSON)
    @AuthRestController
    public Result openPush() {
        boolean ret;
        Result result = new Result();

        ret = resetGrayOpenPushSwitch();
        if (!ret) {
            result.setSuccess(false);
            return result;
        }

        PersistenceData persistenceData = PersistenceDataBuilder.createPersistenceData(ValueConstants.STOP_PUSH_DATA_SWITCH_DATA_ID, "false");
        persistenceData.setData("false");

        try {
            ret = provideDataService.saveProvideData(persistenceData);
            logger.info("close stop push data switch to DB result {}!", ret);
        } catch (Exception e) {
            logger.error("error close stop push data switch from DB!");
            throw new RuntimeException("Close stop push data switch from DB error!");
        }

        if (ret) {
            fireDataChangeNotify(persistenceData.getVersion(), ValueConstants.STOP_PUSH_DATA_SWITCH_DATA_ID);
        }

        result.setSuccess(ret);
        return result;
    }

    @POST
    @Path("grayOpen")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public Result grayOpenPush(GrayOpenPushSwitchRequest request) {
        ObjectMapper mapper = JsonUtils.getJacksonObjectMapper();
        Result result = new Result();
        String data;
        try {
            data = mapper.writeValueAsString(request);
        } catch (JsonProcessingException e) {
            logger.error("encoding gray open request error", e);
            result.setSuccess(false);
            result.setMessage(e.getMessage());
            return result;
        }
        PersistenceData persistenceData = PersistenceDataBuilder.createPersistenceData(ValueConstants.PUSH_SWITCH_GRAY_OPEN_DATA_ID, data);
        boolean ret;
        try {
            ret = provideDataService.saveProvideData(persistenceData);
        } catch (Exception e) {
            result.setSuccess(false);
            result.setMessage(e.getMessage());
            return result;
        }
        if (ret) {
            fireDataChangeNotify(persistenceData.getVersion(), ValueConstants.PUSH_SWITCH_GRAY_OPEN_DATA_ID);
        }
        result.setSuccess(ret);
        return result;
    }

    private boolean resetGrayOpenPushSwitch() {
        PersistenceData persistenceData = PersistenceDataBuilder.createPersistenceData(ValueConstants.PUSH_SWITCH_GRAY_OPEN_DATA_ID, "");
        boolean ret;
        try {
            ret = provideDataService.saveProvideData(persistenceData);
            logger.info("reset gray open push switch to DB result {}!", ret);
        } catch (Exception e) {
            logger.error("error reset gray open push switch to DB!", e);
            throw new RuntimeException("reset gray open push switch to DB error");
        }
        if (ret) {
            fireDataChangeNotify(persistenceData.getVersion(), ValueConstants.PUSH_SWITCH_GRAY_OPEN_DATA_ID);
        }
        return ret;
    }

    private void fireDataChangeNotify(Long version, String dataInfoId) {

        ProvideDataChangeEvent provideDataChangeEvent = new ProvideDataChangeEvent(dataInfoId, version, getNodeTypes());
        if (logger.isInfoEnabled()) {
            logger.info("send PERSISTENCE_DATA_CHANGE_NOTIFY_TASK notifyProvideDataChange: {}", provideDataChangeEvent);
        }
        provideDataNotifier.notifyProvideDataChange(provideDataChangeEvent);
    }

    protected Set<NodeType> getNodeTypes() {
        return new HashSet<>(Arrays.asList(NodeType.client_interface));
    }


    protected StopPushDataResource setProvideDataService(ProvideDataService provideDataService) {
        this.provideDataService = provideDataService;
        return this;
    }


    protected StopPushDataResource setProvideDataNotifier(DefaultProvideDataNotifier provideDataNotifier) {
        this.provideDataNotifier = provideDataNotifier;
        return this;
    }
}
